Skip to content

feat!: expose the timeout tiers on the public API - #1046

Merged
vdusek merged 11 commits into
v3from
feat/timeout-tiers
Sep 13, 2026
Merged

vdusek merged 11 commits into
v3from
feat/timeout-tiers

Conversation

@vdusek

@vdusek vdusek commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Issue

Description

The client already chose a timeout per method internally, but the only public knob was the global timeoutSecs, so a call that legitimately takes longer had no way to ask for more time. The tiers are now public, following the Python client, and every method that sends a request takes a timeoutSecs option.

Tier Default Covers
short 5 s metadata reads and writes
medium 30 s listing, batch and trigger calls
long 360 s downloads, uploads and streaming
noTimeout none polling that waits for a job to finish

Every method keeps the tier its Python counterpart has. timeoutMaxSecs caps a single attempt and bounds the growth across retries, which doubles from the requested value (T, 2T, 4T, 8T). The six waitForFinish methods get the API's one-minute hold on top of their tier, so the client doesn't abort a request the API is still holding and start a second run on retry.

timeoutSecs takes a tier name, a number of seconds, or 'noTimeout'. The unit lives in the option name rather than in the value, the way waitSecs and timeoutMaxSecs already do.

The last commit also drops the leading underscore from non-public members, as agreed in review. Where the underscore was covering a collision with a public member, the helper got a new name: _url to buildUrl, _params to buildParams, _get to getResource, _list to listResources, _waitForFinish to waitForJobFinish, _batchAddRequests to addRequestBatch, and ApifyClient._options to subClientOptions. src/apify_api_error.ts and src/statistics.ts are left for #1057.

Testing

test/client_timeouts.test.ts covers the tier defaults, per-call overrides, the waitForFinish hold, runTimeoutSecs and the requestQueue(id, { timeoutSecs }) cap. test/http_client.test.ts covers the cap warning and the retry growth. test/apify_api_error.test.ts guards clientMethod, which reads the renamed helpers off the stack.

Breaking changes

  • timeoutSecs on the constructor is replaced by timeoutShortSecs, timeoutMediumSecs, timeoutLongSecs and timeoutMaxSecs.
  • Outside the storage clients, methods used the global 360 s and now use short or medium, so a call that waited out a slow API can fail sooner.
  • The run timeout of ActorClient.start() / call(), TaskClient.start() / call() and RunClient.resurrect() is now runTimeoutSecs. TypeScript reports a leftover timeout at compile time, and the option schemas are strict, so JavaScript gets an ArgumentValidationError instead of a silently dropped run limit.
  • client.httpClient.call() takes timeoutSecs in seconds, where the axios field it replaces took milliseconds. A direct call that passed timeout: 30000 becomes timeoutSecs: 30.
  • Methods without options gained an options parameter, and methods that take a payload gained a second one.
  • The protected helpers of ApiClient, ResourceClient and ResourceCollectionClient are renamed, which reaches anyone subclassing them.

KeyValueStoreClient.setRecord() keeps its timeoutSecs, and a plain number still means what it did in v2, so Crawlee's setValue() needs no paired PR. The option now takes a tier name or 'noTimeout' as well. The same goes for client.requestQueue(id, { timeoutSecs }), which caps the tier of every request that queue client sends.

✍️ Drafted by Claude Code

Mirrors the Python client: three tiers (short 5 s, medium 30 s, long 360 s) plus `noTimeout`, each method assigned one, tier durations and a `timeoutMaxSecs` cap set on the constructor, a per-call `timeout` override on every method, and a warning when a requested timeout is capped. The timeout of a retried request now doubles from the requested value on every attempt instead of compounding.

BREAKING CHANGE: The `timeoutSecs` option of `ApifyClient` is replaced by `timeoutShortSecs`, `timeoutMediumSecs`, `timeoutLongSecs` and `timeoutMaxSecs`. Methods outside the storage clients now use the `short` or `medium` tier instead of the global 360 s. Every method that sends a request accepts a `timeout` option, so methods without options gained an options parameter and methods that take a payload gained a second one. The run timeout option of `ActorClient.start()` / `call()`, `TaskClient.start()` / `call()` and `RunClient.resurrect()` is renamed from `timeout` to `runTimeout`. The `timeoutSecs` option of `KeyValueStoreClient.setRecord()` is renamed to `timeout`.
@vdusek vdusek added the t-tooling Issues with this label are in the ownership of the tooling team. label Sep 8, 2026
@vdusek vdusek self-assigned this Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ There are broken links in the documentation.

See more at https://github.com/apify/apify-client-js/actions/runs/34573272623#summary-103179907823

@vdusek
vdusek requested a review from janbuchar September 10, 2026 10:11
@vdusek
vdusek marked this pull request as ready for review September 10, 2026 10:11
@vdusek
vdusek requested a review from szaganek as a code owner September 10, 2026 10:11
Comment thread src/base/resource_client.ts Resolved
Comment thread docs/public-api/apify-client.api.md Resolved
The number a timeout takes is a count of seconds, which the option name now says. `runTimeout` becomes `runTimeoutSecs` for the same reason.

BREAKING CHANGE: the per-call `timeout` option is `timeoutSecs` on every method, `ApifyRequestConfig.timeout` is `timeoutSecs`, and the run timeout of `start()`, `call()` and `resurrect()` is `runTimeoutSecs`.
# Conflicts:
#	src/resource_clients/dataset.ts
@vdusek
vdusek merged commit b78c8bc into v3 Sep 13, 2026
8 checks passed
@vdusek
vdusek deleted the feat/timeout-tiers branch September 13, 2026 16:01
vdusek added a commit that referenced this pull request Sep 14, 2026
Non-public members in this repo carried a leading underscore on top of
the `private` / `protected` keyword. Crawlee v4 dropped that convention
and we agreed to follow it here.

This renames every such member across `src/`, so none are left.
`LoggerActorRedirect._log()` and `_outputWithConsole()` in
`src/resource_clients/log.ts` keep their names, since both come from the
`Logger` base class in `@apify/log`. The code samples in
`CONTRIBUTING.md` and the public API report follow the rename.

## Renamed helpers

The protected helpers on the base clients show up in the API report, so
subclasses calling the old names have to switch. Where a bare name would
collide with a public method, the helper got a suffix.

| Old | New |
| --- | --- |
| `_url` | `buildUrl` |
| `_publicUrl` | `buildPublicUrl` |
| `_params` | `buildParams` |
| `_subResourceOptions` | `subResourceOptions` |
| `_toSafeId` | `toSafeId` |
| `_listPaginatedFromCallback` | `listPaginatedFromCallback` |
| `_get` | `getResource` |
| `_update` | `updateResource` |
| `_delete` | `deleteResource` |
| `_waitForFinish` | `waitForJobFinish` |
| `_list` | `listResources` |
| `_listPaginated` | `listResourcesPaginated` |
| `_create` | `createResource` |
| `_getOrCreate` | `getOrCreateResource` |
| `_batchAddRequests` | `addRequestBatch` |
| `_batchAddRequestsWithRetries` | `addRequestBatchWithRetries` |

The names match what #1046 picked for the files it touches, so whichever
of the two lands second rebases onto near-identical lines.

## ApifyApiError.clientMethod

`clientMethod` scrapes the stack with `\._?([A-Za-z]+)`, and the
underscore was what turned an internal frame like
`ActorCollectionClient._list` into the public-looking `list`. A public
method that returns the promise of a shared helper without awaiting it
leaves only the helper's frame on the async stack, so after the rename
the field would report `listResources` to users. A
`PUBLIC_METHOD_BY_HELPER` map in `apify_api_error.ts` maps helper frames
back to the public method, and `test/apify_api_error.test.ts` covers
both the Node and the browser build.

Closes #1057

*✍️ Drafted by Claude Code*
B4nan pushed a commit that referenced this pull request Sep 30, 2026
### Issue

- Closes #1029
- This is a port of the following:
  - apify/apify-client-python#653
  - apify/apify-client-python#664
  - apify/apify-client-python#962

### Description

The client already chose a timeout per method internally, but the only
public knob was the global `timeoutSecs`, so a call that legitimately
takes longer had no way to ask for more time. The tiers are now public,
following the Python client, and every method that sends a request takes
a `timeoutSecs` option.

| Tier | Default | Covers |
| --- | --- | --- |
| `short` | 5 s | metadata reads and writes |
| `medium` | 30 s | listing, batch and trigger calls |
| `long` | 360 s | downloads, uploads and streaming |
| `noTimeout` | none | polling that waits for a job to finish |

Every method keeps the tier its Python counterpart has. `timeoutMaxSecs`
caps a single attempt and bounds the growth across retries, which
doubles from the requested value (T, 2T, 4T, 8T). The six
`waitForFinish` methods get the API's one-minute hold on top of their
tier, so the client doesn't abort a request the API is still holding and
start a second run on retry.

`timeoutSecs` takes a tier name, a number of seconds, or `'noTimeout'`.
The unit lives in the option name rather than in the value, the way
`waitSecs` and `timeoutMaxSecs` already do.

The last commit also drops the leading underscore from non-public
members, as agreed in review. Where the underscore was covering a
collision with a public member, the helper got a new name: `_url` to
`buildUrl`, `_params` to `buildParams`, `_get` to `getResource`, `_list`
to `listResources`, `_waitForFinish` to `waitForJobFinish`,
`_batchAddRequests` to `addRequestBatch`, and `ApifyClient._options` to
`subClientOptions`. `src/apify_api_error.ts` and `src/statistics.ts` are
left for #1057.

## Testing

`test/client_timeouts.test.ts` covers the tier defaults, per-call
overrides, the `waitForFinish` hold, `runTimeoutSecs` and the
`requestQueue(id, { timeoutSecs })` cap. `test/http_client.test.ts`
covers the cap warning and the retry growth.
`test/apify_api_error.test.ts` guards `clientMethod`, which reads the
renamed helpers off the stack.

## Breaking changes

- `timeoutSecs` on the constructor is replaced by `timeoutShortSecs`,
`timeoutMediumSecs`, `timeoutLongSecs` and `timeoutMaxSecs`.
- Outside the storage clients, methods used the global 360 s and now use
`short` or `medium`, so a call that waited out a slow API can fail
sooner.
- The run timeout of `ActorClient.start()` / `call()`,
`TaskClient.start()` / `call()` and `RunClient.resurrect()` is now
`runTimeoutSecs`. TypeScript reports a leftover `timeout` at compile
time, and the option schemas are strict, so JavaScript gets an
`ArgumentValidationError` instead of a silently dropped run limit.
- `client.httpClient.call()` takes `timeoutSecs` in seconds, where the
axios field it replaces took milliseconds. A direct call that passed
`timeout: 30000` becomes `timeoutSecs: 30`.
- Methods without options gained an options parameter, and methods that
take a payload gained a second one.
- The `protected` helpers of `ApiClient`, `ResourceClient` and
`ResourceCollectionClient` are renamed, which reaches anyone subclassing
them.

`KeyValueStoreClient.setRecord()` keeps its `timeoutSecs`, and a plain
number still means what it did in v2, so Crawlee's `setValue()` needs no
paired PR. The option now takes a tier name or `'noTimeout'` as well.
The same goes for `client.requestQueue(id, { timeoutSecs })`, which caps
the tier of every request that queue client sends.

*✍️ Drafted by Claude Code*
B4nan pushed a commit that referenced this pull request Sep 30, 2026
Non-public members in this repo carried a leading underscore on top of
the `private` / `protected` keyword. Crawlee v4 dropped that convention
and we agreed to follow it here.

This renames every such member across `src/`, so none are left.
`LoggerActorRedirect._log()` and `_outputWithConsole()` in
`src/resource_clients/log.ts` keep their names, since both come from the
`Logger` base class in `@apify/log`. The code samples in
`CONTRIBUTING.md` and the public API report follow the rename.

## Renamed helpers

The protected helpers on the base clients show up in the API report, so
subclasses calling the old names have to switch. Where a bare name would
collide with a public method, the helper got a suffix.

| Old | New |
| --- | --- |
| `_url` | `buildUrl` |
| `_publicUrl` | `buildPublicUrl` |
| `_params` | `buildParams` |
| `_subResourceOptions` | `subResourceOptions` |
| `_toSafeId` | `toSafeId` |
| `_listPaginatedFromCallback` | `listPaginatedFromCallback` |
| `_get` | `getResource` |
| `_update` | `updateResource` |
| `_delete` | `deleteResource` |
| `_waitForFinish` | `waitForJobFinish` |
| `_list` | `listResources` |
| `_listPaginated` | `listResourcesPaginated` |
| `_create` | `createResource` |
| `_getOrCreate` | `getOrCreateResource` |
| `_batchAddRequests` | `addRequestBatch` |
| `_batchAddRequestsWithRetries` | `addRequestBatchWithRetries` |

The names match what #1046 picked for the files it touches, so whichever
of the two lands second rebases onto near-identical lines.

## ApifyApiError.clientMethod

`clientMethod` scrapes the stack with `\._?([A-Za-z]+)`, and the
underscore was what turned an internal frame like
`ActorCollectionClient._list` into the public-looking `list`. A public
method that returns the promise of a shared helper without awaiting it
leaves only the helper's frame on the async stack, so after the rename
the field would report `listResources` to users. A
`PUBLIC_METHOD_BY_HELPER` map in `apify_api_error.ts` maps helper frames
back to the public method, and `test/apify_api_error.test.ts` covers
both the Node and the browser build.

Closes #1057

*✍️ Drafted by Claude Code*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants